import java.math.BigInteger;
import java.util.Random;

/**
 * RSA加解密算法
 * 　　1.随机地选择两个大素数p和q，而且保密；
 * 　　2.计算n=pq，将n公开；
 * 　　3.计算φ(n)=(p-1)(q-1)，对φ(n)保密；
 * 　　4.随机地选取一个正整数e，1<e<φ(n)且(e,φ(n))=1，将e公开；
 * 　　5.根据ed=1(mod φ(n))，求出d，并对d保密；
 * 　　6.加密运算：C=Me(mod n)；
 * 　　7.解密运算：M=Cd(mod n)。
 * 　　注意：在加密运算和解密运算中，M和C的值都必须小于n，也就是说，如果明文（或密文）太大，必须进行分组加密（或解密）。
 * //公钥(n,e)
 * //私钥(n,d)
 * https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
 */
public class RSATest {
    public static void main(String[] args) {
        //随机获得一个素数p
        BigInteger p = BigInteger.probablePrime(new Random().nextInt(100) + 100, new Random());
        //随机获得一个素数q
        BigInteger q = BigInteger.probablePrime(new Random().nextInt(100) + 100, new Random());
        //计算n=pq，将n公开；n越大越难以破解
        BigInteger n = p.multiply(q);
        //计算φ(n)=(p-1)(q-1)，对φ(n)保密；  φ(n)就是小于n的整数中与n互质的数的个数
        BigInteger phi_n = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        //随机地选取一个正整数e，1<e<φ(n)且(e,φ(n))=1，将e公开；一般根据用户账号信息生成
        BigInteger e;
        do {
            e = new BigInteger(new Random().nextInt(phi_n.bitLength() - 1) + 1, new Random());
        } while (e.compareTo(phi_n) != -1 || e.gcd(phi_n).intValue() != 1);
        //5.根据ed=1(mod φ(n))，求出d，并对d保密；
        //1除以e的结果，除以 phi_n，得到余数就是d
        BigInteger d = e.modPow(new BigInteger("-1"), phi_n);

        //公钥(n,e)
        System.out.println("//公钥(n,e)" + n + "" + e);
        //私钥(n,d)
        System.out.println("//私钥(n,d)" + n + "" + d);

        //要传递的信息：比如用户密码
        String password = "password123";
        BigInteger Msg = new BigInteger(String.valueOf(123));
        System.out.println(" //要传递的信息" + Msg);
        //加密后的信息:收到用户输入的密码，进行加密后存入数据库
        BigInteger encrypt = RSATest.encrypt(Msg, n, e);
        System.out.println(" //用公钥加密后的信息" + encrypt);
        //解密后的信息：收到用户输入的密码
        BigInteger decrypt = RSATest.decrypt(encrypt, n, d);
        System.out.println(" //用私钥解密后的信息" + decrypt);

    }

    /**
     * 加密
     * <p> C=M^e(mod n)
     *
     * @param M
     * @param n
     * @param e
     * @return
     */
    public static BigInteger encrypt(BigInteger M, BigInteger n, BigInteger e) {
        return M.modPow(e, n);
    }

    /**
     * 解密
     * <p> M=C^d(mod n)
     *
     * @param C
     * @param n
     * @param d
     * @return
     */
    public static BigInteger decrypt(BigInteger C, BigInteger n, BigInteger d) {
        return C.modPow(d, n);
    }
}
